#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# Copyright (c) 2008 Felix Schwarz <felix.schwarz@oss.schwarz.eu>
#
# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation
# files (the "Software"), to deal in the Software without
# restriction, including without limitation the rights to use,
# copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following
# conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
# OTHER DEALINGS IN THE SOFTWARE.

from unittest import TestCase

from authority.middleware import AuthorityMiddleware
from beaker.middleware import SessionMiddleware

#~ import logging
#~ log = logging.getLogger(__name__)

class DummyWSGIApp(object):
    "A dummy class containing several WSGI apps"

    username = None

    def private(cls, environ, start_response):
        if "REMOTE_USER" in environ:
            cls.username = environ["REMOTE_USER"]
            start_response("200 Ok", [('content-type', 'text/html')])
            return ['Hello %s' % cls.username]
        else:
            cls.username = None
            start_response("401 Unauthorized", [('content-type', 'text/html')])
    private = classmethod(private)


    def public(cls, environ, start_response):
        assert ("REMOTE_USER" not in environ)
        cls.username = "anonymous"
        start_response("200 Ok", [('content-type', 'text/html')])
        return ['Hello']
    public = classmethod(public)

    # TODO: how to get a start_response function pointer suitable for testing?
    def start_response(cls, status, response_headers, exc_info=None):
        pass
    start_response = classmethod(start_response)



class TestBasicAuthWithoutSession(TestCase):
    "Tests for HTTP Basic Auth without a session module."

    def setUp(self):
        DummyWSGIApp.username = None
        # TODO: How to build a minimal WSGI environment
        self.environ =  {'PATH_INFO': '/private', 'QUERY_STRING': '',
                              'REMOTE_ADDR': '127.0.0.1',
                              'REMOTE_HOST': 'localhost',
                              'REQUEST_METHOD': 'GET', }


    def call_dummy_app(self, environ, start_response, fkt=DummyWSGIApp.private):
        authority_config = {'authority.scheme':'httpbasicmatch'}
        auth_middleware = AuthorityMiddleware(fkt, authority_config)
        return auth_middleware(environ, start_response)


    def test_missing_credentials(self):
        "Test a request without credentials is rejected."
        output = self.call_dummy_app(self.environ, DummyWSGIApp.start_response)
        self.assertEqual(None, DummyWSGIApp.username)


    def test_bad_credentials(self):
        "Test a request with bad credentials (foo/foi) is rejected."
        self.environ["HTTP_AUTHORIZATION"] = "Basic Zm9vOmZvaa=="
        output = self.call_dummy_app(self.environ, DummyWSGIApp.start_response)
        self.assertEqual(None, DummyWSGIApp.username)


    def test_good_credentials(self):
        "Test that a requst with good credentials (foo/foo) is accepted."
        self.environ["HTTP_AUTHORIZATION"] = "Basic Zm9vOmZvbw=="
        output = self.call_dummy_app(self.environ, DummyWSGIApp.start_response)
        self.assertEqual("foo", DummyWSGIApp.username)


    def test_anonymous_call(self):
        "Test that calls for public methods don't require any credentials."
        output = self.call_dummy_app(self.environ,
                                     DummyWSGIApp.start_response,
                                     fkt=DummyWSGIApp.public)
        self.assertEqual("anonymous", DummyWSGIApp.username)


    def _test_bad_headers(self):
        "Test that bad/malformed headers won't do any harm."
        headers = ["", "FooBar", "Basic foobar"]
        for header in headers:
            self.environ["HTTP_AUTHORIZATION"] = header
            self.call_dummy_app(self.environ, DummyWSGIApp.start_response)
            self.assertEqual(None, DummyWSGIApp.username)

class TestBasicAuthWithSession(TestBasicAuthWithoutSession):
    "Tests for HTTP Basic Auth with a session module."
    
    def call_dummy_app(self, environ, start_response, fkt=DummyWSGIApp.private):
        authority_config = {'authority.scheme':'httpbasicmatch'}
        auth_middleware = AuthorityMiddleware(fkt, authority_config)
        sess_config = {}
        sess_middleware = SessionMiddleware(auth_middleware, sess_config)
        return auth_middleware(environ, start_response)